/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.icee.myth.server.skill;

import com.icee.myth.config.MapConfig;
import com.icee.myth.server.fighter.Fighter;
import com.icee.myth.protobuf.BattleProtocol.StartSkillProto;
import com.icee.myth.server.battle.BattleMessageType;
import com.icee.myth.utils.Consts;
import com.icee.myth.utils.RandomGenerator;

import java.util.LinkedList;

/**
 *
 * @author liuxianke
 */
public class Skill {
    public final SkillStaticInfo staticInfo;    // 技能配置信息

    public Skill(SkillStaticInfo staticInfo) {
        this.staticInfo = staticInfo;
    }
    
    // 执行技能逻辑
    public void action(Fighter caster) {
        if (caster.battle.needBuildProto) {
            caster.battle.startBattleAction();
            caster.battle.addBattleMessage(BattleMessageType.STARTSKILL, buildStartSkillProto(caster.id).toByteString());
        }

        // 选择技能目标
        Fighter target = (staticInfo.targetType == Consts.SKILL_TARGET_TYPE_SELF)?caster:caster.battle.getTarget(caster, staticInfo.targetChooseType);
        
        // 执行技能打击点
        for (SkillHitPoint hitPoint : staticInfo.hitPoints) {
            // 选择打击点目标
            LinkedList<Fighter> targets = getHitPointTargets(hitPoint, caster, target);

            // 对每个目标作用打击点
            doHitPoint(hitPoint, caster, targets);
        }

        if (caster.battle.needBuildProto) {
            caster.battle.endBattleAction();
        }
    }

    private LinkedList<Fighter> getHitPointTargets(SkillHitPoint hitPoint, Fighter caster, Fighter target) {
        switch (hitPoint.area) {
            case Consts.SKILL_AREA_TYPE_TARGET: {
                LinkedList<Fighter> targets = new LinkedList<Fighter>();
                targets.add(target);
                return targets;
            }
            case Consts.SKILL_AREA_TYPE_SELF: {
                LinkedList<Fighter> targets = new LinkedList<Fighter>();
                targets.add(caster);
                return targets;
            }
            case Consts.SKILL_AREA_TYPE_TARGET_ROW: {
                return caster.battle.getTargetsInRow(target);
            }
            case Consts.SKILL_AREA_TYPE_SELF_ROW: {
                return caster.battle.getTargetsInRow(caster);
            }
            case Consts.SKILL_AREA_TYPE_TARGET_COLUMN: {
                return caster.battle.getTargetsInColumn(target);
            }
            case Consts.SKILL_AREA_TYPE_SELF_COLUMN: {
                return caster.battle.getTargetsInColumn(caster);
            }
            case Consts.SKILL_AREA_TYPE_TARGET_ALL: {
                return caster.battle.getTargetsInTeam(target);
            }
            case Consts.SKILL_AREA_TYPE_SELF_ALL: {
                return caster.battle.getTargetsInTeam(caster);
            }
            case Consts.SKILL_AREA_TYPE_TARGET_CROSS: {
                return caster.battle.getTargetsInCross(target);
            }
            case Consts.SKILL_AREA_TYPE_SELF_CROSS: {
                return caster.battle.getTargetsInCross(caster);
            }
            case Consts.SKILL_AREA_TYPE_TARGET_RANDOM: {
                return caster.battle.getRandomTargets(target, hitPoint.num);
            }
            case Consts.SKILL_AREA_TYPE_SELF_RANDOM: {
                return caster.battle.getRandomTargets(caster, hitPoint.num);
            }
        }

        return null;
    }

    /**
     * 计算战斗击中点
     * @param hitPoint
     * @param caster
     * @param targets
     */
    public void doHitPoint(SkillHitPoint hitPoint, Fighter caster, LinkedList<Fighter> targets) {
        for (Fighter target : targets) {
            if (hitPoint.hurtType == Consts.HURT_TYPE_HEAL) {
               /**********计算cureHp begin*******************************************/
                // 治疗量
                long cureHp = (long)(caster.atk * (1 + caster.cureUp) * hitPoint.k);

                int cureType = Consts.ATTACK_TYPE_HIT;
                // 计算暴击率
                int criRate = (100 + caster.cri)/2;    // 暴击率（千分比）
                // 判断暴击
                int rand = RandomGenerator.INSTANCE.generator.nextInt(1000);
                if (rand < criRate) {
                    cureType = Consts.ATTACK_TYPE_CRI;
                    cureHp = (long) (cureHp * 1.5f);  // 暴击（150%）
                }
                
                // 随机振幅
                if (cureHp > 1) {
                    rand = 90 + RandomGenerator.INSTANCE.generator.nextInt(20);
                    cureHp = (long) (cureHp * ((float)rand / 100));
                }
                target.beCure(caster, cureType, //Consts
                			  cureHp, //cureHp 治疗量
                			  staticInfo.effectId, hitPoint.id);
                /**********计算cureHp end*******************************************/
            } else {
                // 计算命中和暴击
                int hitRate = 900 + caster.hit - target.dod;    // 命中率（千分比）
                int extraCriRate = (hitRate>1000)?(hitRate-1000)/2:0;

                int attackType = Consts.ATTACK_TYPE_DOD;
                int rand = RandomGenerator.INSTANCE.generator.nextInt(1000);
                if (rand < hitRate) {
                    // 判断暴击和格挡
                    int criRate = 100 + caster.cri + extraCriRate - target.ten; // 暴击率（千分比）
                    if (criRate > 0) {
                        rand = RandomGenerator.INSTANCE.generator.nextInt(1000);
                        attackType = (rand < criRate)? Consts.ATTACK_TYPE_CRI: Consts.ATTACK_TYPE_HIT;
                    } else {
                        rand = RandomGenerator.INSTANCE.generator.nextInt(1000);
                        attackType = (rand < -criRate)? Consts.ATTACK_TYPE_PAR: Consts.ATTACK_TYPE_HIT;
                    }
                }

                long attackHp = 0;
                if (attackType != Consts.ATTACK_TYPE_DOD) {
                    // 计算伤害数值
                    if (hitPoint.hurtType == Consts.HURT_TYPE_PHYSICS) {
                        // DAMP=ATK*（1+DAMXstr*（STR-STR）+DAMXsta*（STA-STA））*兵种修正*DAMResP*DAMSkill
                        attackHp = (long) (caster.atk
                                            * (1 + MapConfig.INSTANCE.damXstr * (caster.cardStaticInfo.STR - target.cardStaticInfo.STR) + MapConfig.INSTANCE.damXsta * (caster.cardStaticInfo.STA - target.cardStaticInfo.STA))
                                            * MapConfig.INSTANCE.unitsRestraint[caster.cardStaticInfo.type][target.cardStaticInfo.type]
                                            * (1 - target.damResP)
                                            * hitPoint.k);
                    } else {
                        // DAMM=ATK*（1+DAMXwis*（WIS-WIS）+DAMXsta*（STA-STA））*兵种修正*DAMResM* DAMSkill
                        attackHp = (long) (caster.atk
                                            * (1 + MapConfig.INSTANCE.damXwis * (caster.cardStaticInfo.WIS - target.cardStaticInfo.WIS) + MapConfig.INSTANCE.damXsta * (caster.cardStaticInfo.STA - target.cardStaticInfo.STA))
                                            * MapConfig.INSTANCE.unitsRestraint[caster.cardStaticInfo.type][target.cardStaticInfo.type]
                                            * (1 - target.damResM)
                                            * hitPoint.k);
                    }

                    if (attackType == Consts.ATTACK_TYPE_CRI) {
                        attackHp = (long) (attackHp * 1.5f);
                    } else if (attackType == Consts.ATTACK_TYPE_PAR) {
                        attackHp = (long) (attackHp * 0.5f);
                    }

                    if (attackHp <= 0) {
                        attackHp = 1;
                    }

                    // 随机振幅
                    if (attackHp > 1) {
                        rand = 90 + RandomGenerator.INSTANCE.generator.nextInt(20);
                        attackHp = (long) (attackHp * ((float)rand / 100));
                    }
                }

                target.beAttack(caster, attackType, attackHp, staticInfo.effectId, hitPoint.id);
            }
        }
    }

    private StartSkillProto buildStartSkillProto(int casterId) {
        StartSkillProto.Builder builder = StartSkillProto.newBuilder();
        builder.setActorId(casterId);
        builder.setSkillId(staticInfo.effectId);

        return builder.build();
    }
}
